import cv2 as cv
import numpy as np


class MyHoughLines:
    """
    霍夫直线变换 - 用来做直线检测
    """

    def __init__(self):
        self.img = None
        self.chess_img = None
        self.init_img()

    def init_img(self):
        self.chess_img = cv.imread("img/weiqi.jpg", cv.IMREAD_COLOR)
        cv.imshow("chess_img", self.chess_img)

    def draw_line(self):
        """
        霍夫变换的原理就是做出过某个点的所有直线(在指定rho-距离精度、theta-角度精度下)，
        然后判断在这些直线上的点的数量(threshold)超过一定值就认为这是一条识别到的直线
        :return:
        """
        # 绘制一张黑图
        self.img = np.zeros((500, 500, 1), np.uint8)
        # 绘制一个点
        cv.line(self.img, (10, 10), (10, 10), (255,), 1)
        cv.imshow("点", self.img)

        rho = 1  # 以像素为单位的距离精度
        theta = np.pi / 180  # 每1度做一条直线
        threshold = 0  # 超过几个点后，就认为是一条直线
        lines = cv.HoughLines(self.img, rho, theta, threshold)

        hough_img = self.img.copy()
        for line in lines[:, 0]:
            rho, theta = line
            a = np.cos(theta)
            b = np.sin(theta)
            x = a * rho
            y = b * rho

            x1 = int(np.round(x + 1000 * (-b)))
            y1 = int(np.round(y + 1000 * a))

            x2 = int(np.round(x - 1000 * (-b)))
            y2 = int(np.round(y - 1000 * a))

            cv.line(hough_img, (x1, y1), (x2, y2), (255, 0, 0), 1)

        cv.imshow("hough_img", hough_img)

    def find_liens(self):
        # 二值化
        gray_img = cv.cvtColor(self.chess_img, cv.COLOR_BGR2GRAY)
        flag, thresh_img = cv.threshold(gray_img,100, 255, cv.THRESH_BINARY_INV)

        #  线段以像素为单位的距离精度，double类型的，推荐用1.0
        rho = 1
        # 线段以弧度为单位的角度精度，推荐用numpy.pi/180
        theta = np.pi / 180
        # 累加平面的阈值参数，int类型，超过设定阈值才被检测出线段，值越大，基本上意味着检出的线段越长，检出的线段个数越少。
        threshold = 10
        # 线段以像素为单位的最小长度
        min_line_length = 25
        # 同一方向上两条线段判定为一条线段的最大允许间隔（断裂），超过了设定值，则把两条线段当成一条线段，值越大，允许线段上的断裂越大，越有可能检出潜在的直线段
        max_line_gap = 3
        lines = cv.HoughLinesP(thresh_img, rho, theta, threshold,
                               minLineLength=min_line_length,
                               maxLineGap=max_line_gap)
        hough_img = self.chess_img.copy()

        for line in lines:
            x1, y1, x2, y2 = line[0]
            cv.line(hough_img, (x1, y1), (x2, y2), (0, 0, 255), 2)

        cv.imshow("hough_img", hough_img)

    def find_circle(self):
        """
        霍夫变换找圆
            1. 霍夫圆检测对噪声敏感，先对对象做中值滤波
            2. 检测边缘，先把可能的圆边缘过滤出来
            3. 根据边缘得到最大概率的圆心，进而得到最佳半径
        """
        gray_img = cv.cvtColor(self.chess_img, cv.COLOR_BGR2GRAY)
        # 高斯模糊
        # median_img = cv.GaussianBlur(self.chess_img, (3, 3), 0)
        # 中值滤波
        # median_img = cv.medianBlur(median_img, 5)
        # median_img = cv.cvtColor(median_img, cv.COLOR_BGR2GRAY)
        # cv.imshow("median_img", median_img)

        """
        @param 8-bit 单通道图片
        @param method 检测方法, 当前只有cv2.HOUGH_GRADIENT(梯度下降算法)
        @param dp 累加器分辨率和图像分辨率的反比例, 例如:
            如果 dp=1 累加器和输入图像分辨率相同. 
            如果 dp=2 累加器宽高各为输入图像宽高的一半相同. 
        @param minDist 检测到圆的圆心之间的最小距离。
            如果参数太小，除了真实的一个之外，可能错误地检测到多个相邻的圆圈。
            如果参数太大，可能会遗漏一些圆
        @param param1 参数1。它是两个传递给Canny边缘检测器的较高阈值（较低的阈值是此值的一半）
        @param param2 参数2, 它是检测阶段圆心的累加器阈值。
            它越小，会检测到更多的假圆圈。较大累加器值对应的圆圈将首先返回。
        @param minRadius 最小圆半径.
        @param maxRadius 最大圆半径. 
            如果<=0, 会使用图片最大像素值
            如果< 0, 直接返回圆心, 不计算半径
        """
        circles = cv.HoughCircles(gray_img, cv.HOUGH_GRADIENT,
                                  dp=1,
                                  minDist=20,
                                  param1=70,
                                  param2=30,
                                  minRadius=0,
                                  maxRadius=20)

        circles = np.uint16(np.around(circles))
        for circle in circles[0, :]:
            # 画外圆
            cv.circle(self.chess_img, (circle[0], circle[1]), circle[2], (0, 255, 0), 2)
            # 画内圆 - 圆心
            cv.circle(self.chess_img, (circle[0], circle[1]), 2, (0, 0, 255), 3)

        cv.imshow("find_circle", self.chess_img)


if __name__ == '__main__':
    MyHoughLines().find_liens()
    # MyHoughLines().find_circle()
    # 让程序处于等待推出状态
    cv.waitKey(0)
    # 当程序推出时，释放所有窗口资源
    cv.destroyAllWindows()